#pragma once 
#include <iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <cstdlib>
#include <cstring>
#include <string>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
using namespace std;
#include "log.hpp"
#include "ThreadPool.hpp"
#include "Task.hpp"
#include "daemon.hpp"
const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 5; //一般不要太大
Log lg;

enum
{
    UsageError = 1,
    SocketError,
    BindError,
    ListenError
};
class TcpServer;

class ThreadData
{
public:
    ThreadData(int fd, const std::string& ip, const uint16_t port, TcpServer* t)
        :sockfd(fd)
        ,clientip(ip)
        ,clientport(port)
        ,tsvr(t)
    {}
public: 
    int sockfd;
    string clientip;
    uint16_t clientport;
    TcpServer* tsvr;
};

class TcpServer
{
public:
    TcpServer(const uint16_t& port, const std::string& ip = defaultip)
        :listensockfd_(defaultfd)
        ,port_(port)
        ,ip_(ip)
    {}
    void InitServer()
    {
        listensockfd_ = socket(AF_INET, SOCK_STREAM, 0);
        if(listensockfd_ < 0)
        {
            lg(Fatal, "create socket error, errno: %d, errstring: %s", errno, strerror(errno));
            exit(SocketError);
        }
        lg(Info, "create socket success, listensockfd_: %d", listensockfd_);

        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_);
        inet_aton(ip_.c_str(), &(local.sin_addr));

        if(bind(listensockfd_, (const struct sockaddr*)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind error, errno: %d, errstring: %s", errno, strerror(errno));
            exit(BindError);
        }
        lg(Info, "bind socket success, listensockfd_: %d", listensockfd_);
        
        //Tcp是面向连接的，服务器一般是比较“被动”的,一直在等待连接到来的
        if(listen(listensockfd_, backlog) < 0)
        {
            lg(Fatal, "listen error, errno: %d, errstring: %s", errno, strerror(errno));
            exit(ListenError);
        }        
        lg(Info, "listen socket success, listensockfd_: %d", listensockfd_);

    }
    //version 3
    // static void* Routine(void* args)
    // {
    //     pthread_detach(pthread_self());
    //     //线程就不需要关于多余的文件描述符了，因为它就没有多余的，线程之间是共享的，不是像进程一样拷贝得到的
    //     //相反的是,它还需要专门将这些参数传进来
    //     ThreadData* td = static_cast<ThreadData*>(args);
    //     td->tsvr->Service(td->sockfd, td->clientip, td->clientport);
    //     delete td;
    //     return nullptr;
    // }

    void Start()
    {
        lg.Enable(Classfile);
        Daemon();

        //防止写的时候,读端的文件描述符被关了
        //signal(SIGPIPE, SIG_IGN);


        //线程池启动 -- version 4
        ThreadPool<Task>::GetInstance()->Start();
        

        //当子进程退出时，系统会发出SIGCHLD信号。如果父进程对其默认处理（即什么也不做），子进程退出后在系统中的状态信息就不会被释放，这种进程被称为僵尸进程。
        //signal(SIGCHLD, SIG_IGN)告诉系统父进程愿意忽略这个信号，一旦定义了这个行为，子进程的状态信息就能正常地被系统释放，避免了僵尸进程的产生
        //signal(SIGCHLD, SIG_IGN); --多进程版用到这个 version 2
        cout << "tcpServer is running .... " << endl;
        for(;;)
        {
            //1.获取新连接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(listensockfd_, (struct sockaddr*)&client, &len);
            if(sockfd < 0)
            {
                lg(Fatal, "accept error, errno: %d, errstring: %s", errno, strerror(errno));
            }

            uint16_t clientport = ntohs(client.sin_port);
            char clientip[32];
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));

            lg(Info, "get a new link..., sockfd: %d, client ip: %s, client port: %d\n", sockfd, clientip, clientport);
         
            //version 1 --- 单进程版
            //Service(sockfd, ipstr, clientport);
            //close(sockfd);
        
            //version 2 --- 多进程版 --- 创建一个进程成本太高了
            // pid_t id = fork();
            // if(id == 0)
            // {
            //     //child;
            //     //子进程可以看到父进程的文件描述符，因为会将父进程的文件描述符给拷贝过去
            //     close(listensockfd_); //这个是不需要的，对于我们的子进程只需要拿着sockfd进行通信就可以了，不需要这个listensockfd
            //     if(fork() > 0) //fork返回一个pid,这个pid大于0,也就是让子进程退出了
            //     {
            //         exit(0);
            //     }
            //     //孙子进程去执行任务。它已经被1号进程接管了，也就是这是一个孤儿进程，被system 领养了。这样的话，我们就不需要专门去回收处理这个进程了
            //     Service(sockfd, clientip, clientport);
            //     close(sockfd);
            //     exit(0);
            // }
            // //father
            // close(sockfd); //父进程也就不关心sockfd，只有子进程会使用它。
            // pid_t rid = waitpid(id, nullptr, 0);
            // (void)rid;

            //version 3 --- 多线程版
            // ThreadData* td = new ThreadData(sockfd, clientip, clientport, this); 
            // pthread_t tid;
            // pthread_create(&tid, nullptr, Routine, td);

            //version 4 --- 线程池版
            Task t(sockfd, clientip, clientport);
            ThreadPool<Task>::GetInstance()->Push(t);
        }
    }
    // version 1 2 3
    // void Service(int sockfd, const std::string& clientip, const uint16_t& clientport)
    // {
    //     char buffer[4096];
    //     while(true)
    //     {
    //         ssize_t n = read(sockfd, buffer, sizeof(buffer));
    //         if(n > 0)
    //         {
    //             buffer[n] = 0;
    //             cout << "client say# " << buffer << endl;
    //             string echo_string = "tcpserver echo# ";
    //             echo_string += buffer;
    //             write(sockfd, echo_string.c_str(), echo_string.size());
    //         }
    //         else if(n == 0)
    //         {
    //             lg(Info, "%s:%d quit, server close sockfd: %d", clientip.c_str(), clientport, sockfd);
    //             break;
    //         }
    //         else 
    //         {
    //             lg(Fatal,"read error, sockfd: %d, client ip : %s , client port : %d", sockfd, clientip.c_str(), clientport);
    //             break;
    //         }
    //     }
    // }


    ~TcpServer()
    {

    }
private:
    int listensockfd_;
    uint16_t port_;
    std::string ip_;
};
